/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2022. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * Description:
 * Author: huawei
 * Create: 2019-10-15
 */
#include <linux/types.h>

#include "svm_cmd.h"
#include "devmm_common.h"
#include "devmm_proc_info.h"
#include "devmm_page_cache.h"
#include "devmm_channel.h"
#include "devdrv_interface.h"
#include "svm_heap_mng.h"
#include "svm_proc_mng.h"

int devmm_svm_check_addr_valid(struct devmm_svm_process_id *process_id, u64 addr, u64 size)
{
    struct devmm_svm_process *svm_process = NULL;
    struct devmm_svm_heap *heap = NULL;
    u32 *page_bitmap = NULL;
    int ret;

    devmm_drv_debug("Check address details. (addr=0x%llx; size=%llu)\n", addr, size);

    ret = devmm_svm_get_svm_proc_and_heap(process_id, addr, &svm_process, &heap);
    if (ret != 0) {
        devmm_drv_err("Process exit or heap error. (hostpid=%d; devid=%u; vfid=%u; va=0x%llx)\n",
                      process_id->hostpid, process_id->devid, process_id->vfid, addr);
        return ret;
    }

    page_bitmap = devmm_get_page_bitmap_with_heap(heap, addr);
    if ((page_bitmap == NULL) || (devmm_check_va_add_size_by_heap(heap, addr, size) != 0)) {
        devmm_drv_err("Bitmap is error. (va=0x%llx; size=%llu)\n", addr, size);
        return -EADDRNOTAVAIL;
    }
    if (!devmm_page_bitmap_is_page_available(page_bitmap) ||
        !devmm_page_bitmap_is_dev_mapped(page_bitmap)) {
        return -EADDRNOTAVAIL;
    }

    return 0;
}

int devmm_shm_check_addr_valid(struct devmm_svm_process_id *process_id, u64 addr, u64 size)
{
    return -EINVAL;
}

static int devmm_get_host_addr_by_dev_pa(u32 dev_id, u64 va, u64 dev_pa, u64* host_addr)
{
    int ret = 0;

    /* read only addr just return dev pa, otherwise host can access by bar addr */
    if ((va >= DEVMM_READ_ONLY_ADDR_START) && (va <= DEVMM_READ_ONLY_ADDR_END)) {
        *host_addr = dev_pa;
    } else {
        ret = devdrv_devmem_addr_d2h(dev_id, (phys_addr_t)dev_pa, (phys_addr_t *)host_addr);
    }
    return ret;
}

int devmm_svm_get_and_pin_pa_list(struct devmm_svm_process_id *process_id, u64 aligned_va, u64 aligned_size,
    u64 *pa_list, u32 pa_num)
{
    struct devmm_svm_process *svm_process = NULL;
    u32 logic_id, dev_id, i, page_num, page_size;
    struct devmm_svm_heap *heap = NULL;
    u32 *page_bitmap = NULL;
    u64 dev_pa, va;
    int ret;

    ret = devmm_svm_get_svm_proc_and_heap(process_id, aligned_va, &svm_process, &heap);
    if (ret != 0) {
        devmm_drv_err("Process exit or heap error. (hostpid=%d; devid=%u; vfid=%u; va=0x%llx)\n",
                      process_id->hostpid, process_id->devid, process_id->vfid, aligned_va);
        return -EINVAL;
    }

    page_bitmap = devmm_get_page_bitmap_with_heap(heap, aligned_va);
    if ((page_bitmap == NULL) || (devmm_check_va_add_size_by_heap(heap, aligned_va, aligned_size) != 0)) {
        devmm_drv_err("Bitmap is NULL. (hostpid=%d; devid=%u; vfid=%u; va=0x%llx; size=%llx)\n",
            process_id->hostpid, process_id->devid, process_id->vfid, aligned_va, aligned_size);
        return -EINVAL;
    }
    page_size = (heap->heap_type == DEVMM_HEAP_HUGE_PAGE) ? devmm_svm->device_hpage_size
                                                          : devmm_svm->device_page_size;
    page_num = (u32)(aligned_size / page_size);
    if ((page_num > pa_num) || (page_num == 0)) {
        devmm_drv_err("Page_num is invalid. (hostpid=%d; devid=%u; vfid=%u; va=0x%llx; page_num=%u; pa_num=%u)\n\n",
            process_id->hostpid, process_id->devid, process_id->vfid, aligned_va, page_num, pa_num);
        return -EINVAL;
    }

    logic_id = devmm_page_bitmap_get_devid(page_bitmap);
    dev_id = devmm_get_phyid_devid_from_svm_process(svm_process, logic_id);
    for (i = 0, va = aligned_va; i < page_num; i++, va += page_size) {
        ret = devmm_find_pa_cache(svm_process, logic_id, va, page_size, &dev_pa);
        if (ret != 0) {
            devmm_drv_err("Find pa cache failed. (hostpid=%d; devid=%u; vfid=%u; va=0x%llx; num=%u)\n",
                          process_id->hostpid, process_id->devid, process_id->vfid, aligned_va, i);
            return ret;
        }
        if (devmm_get_host_addr_by_dev_pa(dev_id, va, dev_pa, &pa_list[i]) != 0) {
            devmm_drv_err("Convert host address failed. (pid=%u; devid=%u; va=0x%llx; num=%u)\n",
                process_id->hostpid, logic_id, aligned_va, i);
            return -ENOMEM;
        }
    }

    /*
     * inc ref of cur va to avoid free addr when it is in used.
     */
    if (devmm_inc_page_ref(svm_process, aligned_va, aligned_size) != 0) {
        devmm_drv_err("Address reference failed. (pid=%u; devid=%u; va=0x%llx; num=%u)\n",
            process_id->hostpid, logic_id, aligned_va, i);
        return -EINVAL;
    }

    return 0;
}

int devmm_shm_get_pa_list(struct devmm_svm_process_id *process_id, u64 aligned_va, u64 aligned_size,
    u64 *pa_list, u32 pa_num)
{
    return -EINVAL;
}

void devmm_svm_put_pa_list(struct devmm_svm_process_id *process_id, u64 va, u64 *pa_list, u32 pa_num)
{
    struct devmm_svm_process *svm_process = NULL;
    u64 size = pa_num << PAGE_SHIFT;

    svm_process = devmm_get_svm_proc(process_id);
    if (svm_process == NULL) {
        devmm_drv_err("Process is exit. (va=0x%llx; hostpid=%d; devid=%u; vfid=%u)\n",
                      va, process_id->hostpid, process_id->devid, process_id->vfid);
        return;
    }
    devmm_dec_page_ref(svm_process, va, size);
}

void devmm_shm_put_pa_list(struct devmm_svm_process_id *process_id, u64 va, u64 *pa_list, u32 pa_num)
{
}

bool devmm_svm_need_ib_register_peer(void)
{
    return false;
}
EXPORT_SYMBOL(devmm_svm_need_ib_register_peer);
